///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef _Resource_H__
#define _Resource_H__

#include "OgrePrerequisites.h"
#include "OgreString.h"
#include "OgreSharedPtr.h"
#include "OgreStringInterface.h"
#include "OgreAtomicWrappers.h"

namespace Ogre {

    typedef unsigned long long int ResourceHandle;


	// Forward declaration
	class ManualResourceLoader;

	
	
	
	class _OgreExport Resource : public StringInterface, public MemAlloc_Resource
    {
	public:
		OGRE_AUTO_MUTEX // public to allow external locking
		class Listener
		{
		public:
		   	Listener() {}
			virtual ~Listener() {}

			
			virtual void backgroundLoadingComplete(Resource*) {}

			
			virtual void backgroundPreparingComplete(Resource*) {}

			
			virtual void loadingComplete(Resource*) {}


			
			virtual void preparingComplete(Resource*) {}

			
			virtual void unloadingComplete(Resource*) {}
		};
		
		/// Enum identifying the loading state of the resource
		enum LoadingState
		{
            /// Not loaded
            LOADSTATE_UNLOADED,
            /// Loading is in progress
            LOADSTATE_LOADING,
            /// Fully loaded
            LOADSTATE_LOADED,
            /// Currently unloading
            LOADSTATE_UNLOADING,
            /// Fully prepared
            LOADSTATE_PREPARED,
            /// Preparing is in progress
            LOADSTATE_PREPARING
		};
    protected:
		/// Creator
		ResourceManager* mCreator;
		/// Unique name of the resource
        String mName;
		/// The name of the resource group
		String mGroup;
		/// Numeric handle for more efficient look up than name
        ResourceHandle mHandle;
		/// Is the resource currently loaded?
        AtomicScalar<LoadingState> mLoadingState;
		/// Is this resource going to be background loaded? Only applicable for multithreaded
		volatile bool mIsBackgroundLoaded;
		/// The size of the resource in bytes
        size_t mSize;
		/// Is this file manually loaded?
		bool mIsManual;
		/// Origin of this resource (e.g. script name) - optional
		String mOrigin;
		/// Optional manual loader; if provided, data is loaded from here instead of a file
		ManualResourceLoader* mLoader;
		/// State count, the number of times this resource has changed state
		size_t mStateCount;

		typedef set<Listener*>::type ListenerList;
		ListenerList mListenerList;
		OGRE_MUTEX(mListenerListMutex)

		
		Resource() 
			: mCreator(0), mHandle(0), mLoadingState(LOADSTATE_UNLOADED), 
			mIsBackgroundLoaded(false),	mSize(0), mIsManual(0), mLoader(0)
		{ 
		}

		
		virtual void preLoadImpl(void) {}
		
		virtual void postLoadImpl(void) {}

		
		virtual void preUnloadImpl(void) {}
		
		virtual void postUnloadImpl(void) {}

        
        virtual void prepareImpl(void) {}
        
        virtual void unprepareImpl(void) {}
		
		virtual void loadImpl(void) = 0;
		
		virtual void unloadImpl(void) = 0;
		
		virtual size_t calculateSize(void) const = 0;

    public:
		
		Resource(ResourceManager* creator, const String& name, ResourceHandle handle,
			const String& group, bool isManual = false, ManualResourceLoader* loader = 0);

        
        virtual ~Resource();

        
        virtual void prepare(bool backgroundThread = false);

     
        virtual void load(bool backgroundThread = false);

		
		virtual void reload(void);

        
        virtual bool isReloadable(void) const
        {
            return !mIsManual || mLoader;
        }

        
		virtual bool isManuallyLoaded(void) const
		{
			return mIsManual;
		}

		
		virtual void unload(void);

        
        virtual size_t getSize(void) const
        { 
            return mSize; 
        }

        
        virtual void touch(void);

        
        virtual const String& getName(void) const 
        { 
            return mName; 
        }

        virtual ResourceHandle getHandle(void) const
        {
            return mHandle;
        }

        
        virtual bool isPrepared(void) const 
        { 
			// No lock required to read this state since no modify
            return (mLoadingState.get() == LOADSTATE_PREPARED); 
        }

        
        virtual bool isLoaded(void) const 
        { 
			// No lock required to read this state since no modify
            return (mLoadingState.get() == LOADSTATE_LOADED); 
        }

		
		virtual bool isLoading() const
		{
			return (mLoadingState.get() == LOADSTATE_LOADING);
		}

		
		virtual LoadingState getLoadingState() const
		{
			return mLoadingState.get();
		}



		
		virtual bool isBackgroundLoaded(void) const { return mIsBackgroundLoaded; }

		
		virtual void setBackgroundLoaded(bool bl) { mIsBackgroundLoaded = bl; }

		
		virtual void escalateLoading();

		
		virtual void addListener(Listener* lis);

		
		virtual void removeListener(Listener* lis);

		/// Gets the group which this resource is a member of
		virtual const String& getGroup(void) const { return mGroup; }

		
		virtual void changeGroupOwnership(const String& newGroup);

		/// Gets the manager which created this resource
		virtual ResourceManager* getCreator(void) { return mCreator; }
		
		virtual const String& getOrigin(void) const { return mOrigin; }
		/// Notify this resource of it's origin
		virtual void _notifyOrigin(const String& origin) { mOrigin = origin; }

		
		virtual size_t getStateCount() const { return mStateCount; }

		
		virtual void _dirtyState();


		
		virtual void _fireLoadingComplete(bool wasBackgroundLoaded);

		
		virtual void _firePreparingComplete(bool wasBackgroundLoaded);

		
		virtual void _fireUnloadingComplete(void);


    };

	
	typedef SharedPtr<Resource> ResourcePtr;

	
	class _OgreExport ManualResourceLoader
	{
	public:
		ManualResourceLoader() {}
		virtual ~ManualResourceLoader() {}

        
		virtual void prepareResource(Resource* resource)
                { (void)resource; }

		
		virtual void loadResource(Resource* resource) = 0;
	};
	
	
}

#endif
